<?php
class CF_App_Controller
{
	/**
	 * Path to controller folder
	 * @var array
	 */
	protected $_path = array();
	
	/**
	 * 
	 * @var CF_App_Controller_Request
	 */
	protected $_request = null;
	
	/**
	 * 
	 * @var unknown_type
	 */
	protected $_response = null;
	
	/**
	 * 
	 * @var string
	 */
	protected $_moduleKey = 'module';
	
	/**
	 * 
	 * @var string
	 */
	protected $_defaultModule = 'index';
	
	/**
	 * 
	 * @var string
	 */
	protected $_controllerKey = 'controller';
	
	/**
	 * 
	 * @var string
	 */
	protected $_defaultController = 'index';
	
	/**
	 * 
	 * @var string
	 */
	protected $_actionKey = 'action';
	
	/**
	 * 
	 * @var string
	 */
	protected $_defaultAction = 'index';
	
	/**
	 * 
	 * @var string
	 */
	protected $_controllerClassFormat = '%sController';
	
	/**
	 * 
	 * @var string
	 */
	protected $_actionMethodFormat = '%sAction';
	
	/**
	 * 
	 * @var string
	 */
	protected $_module = null;
	
	/**
	 * 
	 * @var string
	 */
	protected $_controller = null;
	
	/**
	 * 
	 * @var string
	 */
	protected $_action = null;
	
	/**
	 * @return void
	 */
	public function __construct()
	{
		
	}
	
	public function getPath()
	{
		return $this->_path;
	}
	
	public function addPath($path)
	{
		$this->_path[] = $path;
		return $this;
	}
	
	/**
	 * @return CF_App_Controller_Request
	 */
	public function getRequest()
	{
		if(null === $this->_request){
			$this->_request = new CF_App_Controller_Request();
		}
		return $this->_request;
	}
	
	/**
	 * 
	 * @param CF_App_Controller_Request $request
	 * @return CF_App_Controller $this
	 */
	public function setRequest(CF_App_Controller_Request $request)
	{
		$this->_request = $request;
		return $this;
	}
	
	/**
	 * @return CF_App_Controller_Response_Abstract
	 */
	public function getResponse()
	{
		if(null === $this->_response){
			$this->_response = new CF_App_Controller_Response();
		}
		return $this->_response;
	}
	
	/**
	 * 
	 * @param CF_App_Controller_Response_Abstract $response
	 * @return CF_App_Controller $this
	 */
	public function setResponse(CF_App_Controller_Response_Abstract $response)
	{
		$this->_response = $response;
		return $this;
	}
	
	/**
	 * @return string
	 */
	public function getModuleKey()
	{
		return $this->_moduleKey;
	}
	
	/**
	 * 
	 * @param string $value
	 * @return CF_App_Controller $this
	 */
	public function setModuleKey($value)
	{
		$this->_moduleKey = $value;
		return $this;
	}
	
	/**
	 * @return string
	 */
	public function getControllerKey()
	{
		return $this->_controllerKey;
	}
	
	/**
	 * 
	 * @param string $value
	 * @return CF_App_Controller $this
	 */
	public function setControllerKey($value)
	{
		$this->_controllerKey = $value;
		return $this;	
	}
	
	/**
	 * @return string
	 */
	public function getControllerClassFormat()
	{
		return $this->_controllerClassFormat;
	}
	
	/**
	 * 
	 * @param string $format
	 * @return CF_App_Controller $this
	 */
	public function setControllerClassFormat($format)
	{
		$this->_controllerClassFormat = $format;
		return $this;
	}
	
/**
	 * @return string
	 */
	public function getActionMethodFormat()
	{
		return $this->_actionMethodFormat;
	}
	
	/**
	 * 
	 * @param string $format
	 * @return CF_App_Controller $this
	 */
	public function setActionMethodFormat($format)
	{
		$this->_actionMethodFormat = $format;
		return $this;
	}
	
	/**
	 * @return string
	 */
	public function getActionKey()
	{
		return $this->_actionKey;
	}
	
	/**
	 * 
	 * @param string $value
	 * @return CF_App_Controller $this
	 */
	public function setActionKey($value)
	{
		$this->_actionKey = $value;
	}
	
	/**
	 * @return string
	 */
	public function getModule()
	{
		if(null == $this->_module){
			$this->_module = $this->getRequest()->get($this->_moduleKey);
			if(!$this->_module) $this->_module = $this->_defaultModule;
		}
		
		return $this->_module;
	}
	
	/**
	 * 
	 * @param string $name
	 * @return CF_App_Controller $this
	 */
	public function setModule($name)
	{
		$this->_module = $name;
		return $this;
	}
	
	/**
	 * @return string
	 */
	public function getController()
	{
		if(null == $this->_controller){
			$this->_controller = $this->getRequest()->get($this->_controllerKey);
			if(!$this->_controller) $this->_controller = $this->_defaultController;
		}
		
		return $this->_controller;
	}
	
	/**
	 * 
	 * @param string $name
	 * @return CF_App_Controller $this
	 */
	public function setController($name)
	{
		$this->_controller = $name;
		return $this;
	}
	
	/**
	 * @return string
	 */
	public function getAction()
	{
		if(null == $this->_action){
			$this->_action = $this->getRequest()->get($this->_actionKey);
			if(!$this->_action) $this->_action = $this->_defaultAction;
		}
		return $this->_action;
	}
	
	/**
	 * 
	 * @param string $name
	 * @return CF_App_Controller $this
	 */
	public function setAction($name)
	{
		$this->_action = $name;
		return $this;
	}
	
	/**
	 * @return string
	 */
	public function getControllerClassName()
	{
		return ucfirst(sprintf($this->_controllerClassFormat, $this->getController()));
	}
	
	/**
	 * @return string
	 */
	public function getActionMethodName()
	{
		return sprintf($this->_actionMethodFormat, $this->getAction());
	}
	
	/**
	 * @return object
	 */
	public function getControllerClass()
	{
		$className = $this->getControllerClassName();
		
		# check file exist
		$has = false;
		foreach($this->_path as $path){
			if(is_file($path . $className . '.php')){
				require_once $path . $className . '.php';
				$has = true;
				break;
			}
		}
		
		if(!$has){
			throw new CF_App_Exception("Controller file \"$className.php\" does not exist");
		}
		
		# check class exist
		if(!class_exists($className, false)){
			throw new CF_Acl_Exception("Controller class \"$className\" does not exist");
		}
		
		return new $className;
	}
	
	public function hasActionMethod()
	{
		$controllerClasssName = $this->getControllerClassName();
		$actionMethod = $this->getActionMethodName();
		$ref = new ReflectionClass($controllerClasssName);
		
		if($ref->hasMethod($actionMethod) && $ref->getMethod($actionMethod)->isPublic()){
			return true;
		}
		
		return false;
	}
	
	/**
	 * @return void
	 */	
	public function dispatch()
	{
		$controllerName = $this->getController();
		$actionName = $this->getAction();
		
		$controllerClass = $this->getControllerClass();
		$actionMethod = $this->getActionMethodName();
		
		if(!$this->hasActionMethod()){
			throw new CF_Acl_Exception(
				"Action \"$actionName\" does not exist in controller \"$controllerName\""
			);
		}
		
		# call action
		$res = call_user_func(array($controllerClass, $actionMethod));
		
		if($this->getResponse() instanceof CF_App_Controller_Response){
			$content = CF_App::getInstance()->getView()->render($actionName, $controllerName);
			if($content) $this->getResponse()->setBody($content);
		}else{
			$this->getResponse()->setBody($res);
		}
		$this->getResponse()->send();
	}
}